﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;

using System.Windows.Media;
using System.Text.RegularExpressions;
using NicoView.Model.Service;
using NicoView.Extentions;


using System.Windows.Input;

using NicoView.Properties;
using NicoView.ViewModel;
using NicoView.Model.Service.Comments;
using GalaSoft.MvvmLight.Command;
using NicoView.ViewModel.Message;
using NicoView.Model.Share.Service;

namespace NicoView.View.FlowDocuments
{
    public class BindableNicoCommentParagraph:Paragraph
    {

        public static readonly DependencyProperty BoundTextProperty = DependencyProperty.Register("BoundComment", typeof(NicoViewCommentViewModel), typeof(BindableNicoCommentParagraph), new PropertyMetadata(OnBoundTextChanged));

        private NicoViewCommentViewModel comment;

        private Color nameColor;

        private Color planeTextColor;

        static BindableNicoCommentParagraph()
        {
            

            underMinus = 1;

            planeTextPlus = 3;

            nameTextPlus = 2;

            //PLANETEXT_REPLACE_TO_EMPTY_REGEX = new Regex(@"<[aA][^>]*>|</[aA]\s*>");


           
        }
        
        public BindableNicoCommentParagraph()
        {
            Helpers.FixupDataContext(this);
            initialize();
            this.comment = null;
            this.nameColor = default(Color);
            this.planeTextColor = default(Color);
            
        }

        ~BindableNicoCommentParagraph()
        {

        }

        public NicoViewCommentViewModel BoundComment
        {
            get { return (NicoViewCommentViewModel)GetValue(BoundTextProperty); }
            set { SetValue(BoundTextProperty, value); }
        }

        private static void OnBoundTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BindableNicoCommentParagraph)d).SetBoundComment((NicoViewCommentViewModel)e.NewValue);
        }

        private void SetBoundComment(NicoViewCommentViewModel comment)
        {
            OnParseBountText(comment);

        }

        private void initialize()
        {
            //no表示オブジェクト設定
            noRun = new Run();
            noRun.Foreground = new SolidColorBrush(Colors.Black);





            //名前表示オブジェクト設定
            nameRun = new Run();
            namelink = new Hyperlink(nameRun);
            namelink.TextDecorations = null;
            namelink.FontSize += nameTextPlus;
            namelink.ToolTip = USER_INFO_TOOLTIP;
            
            //id表示オブジェクト設定
            idRun = new Run();
            idlink = new Hyperlink(idRun);
            idlink.TextDecorations = null;
            idlink.Foreground = new SolidColorBrush(Colors.LightGray);
            idlink.ToolTip = USER_INFO_TOOLTIP;

            bodySpan = new Span();
            

            displayTime = new Run();
            displayTime.FontSize -= underMinus;
            displayTime.Foreground = new SolidColorBrush() { Color = Colors.Black };
            replyRun = new Run();

            replylink = new Hyperlink(replyRun);
            replylink.TextDecorations = null;
            replylink.FontSize -= underMinus;
            replylink.Foreground = new SolidColorBrush() { Color = Colors.DeepSkyBlue };

            scoreDescriptionRun = new Run();


            Inlines.Add(this.noRun);
            Inlines.Add(this.createBlank());
            Inlines.Add(this.namelink);
            Inlines.Add(this.createBlank());
            Inlines.Add(this.idlink);
            Inlines.Add(this.createBlank());
            Inlines.Add(this.scoreDescriptionRun);
            Inlines.Add(this.createLineBreak());
            Inlines.Add(this.bodySpan);
            Inlines.Add(this.createLineBreak());
            Inlines.Add(this.displayTime);
            Inlines.Add(this.createBlank());
            Inlines.Add(this.replylink);
            
        }


        private Run createBlank()
        {
            return new Run("  ");
        }

        private LineBreak createLineBreak()
        {
            return new LineBreak();
        }

        private ICommand getShowUserCommand(NicoViewCommentViewModel newComment)
        {
            
            return newComment.ShowSingleUserCommand;

        }


        private void OnParseBountText(NicoViewCommentViewModel newComment)
        {

            

            
            updateElement(newComment);

            this.comment = newComment;

            
            
        }

        private void onPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            updateElement(this.comment);
        }


        private string noRunText
        {
            get
            {
                return this.noRun.Text;
            }
            set
            {
                if (this.noRun.Text == value) return;
                this.noRun.Text = value;
            }
        }

        private string nameRunText
        {
            get
            {
                return this.nameRun.Text;
            }

            set
            {
                if (this.nameRun.Text == value) return;
                this.nameRun.Text = value;
            }
        }

        private ICommand nameLinkCommand
        {
            set
            {
                if (namelink.Command == value) return;
                namelink.Command = value;
            }
        }

        private SolidColorBrush namelinkForeground
        {
            set
            {
                if (namelink.Foreground == value) return;
                namelink.Foreground = value;
            }
        }

        

        private string idRunText
        {
            set
            {
                if (idRun.Text == value) return;
                idRun.Text = value;
            }
        }

        private ICommand idlinkCommand
        {
            set
            {
                if (idlink.Command == value) return;
                idlink.Command = value;
            }
        }

        private ICommand replylinkCommand
        {
            set
            {
                if (replylink.Command == value) return;
                replylink.Command = value;
            }
        }

        private string displayTimeText
        {
            set
            {
                if (displayTime.Text == value) return;
                displayTime.Text = value;
            }
        }

        private SolidColorBrush getBrush(Color color)
        {
            if (!brushCash.ContainsKey(color))
            {
                brushCash.Add(color, new SolidColorBrush(color));
            }


            return brushCash[color];
        }

        private void updateElement(NicoViewCommentViewModel newComment)
        {

            if (newComment == null) return;


            noRunText = newComment.DisplayNo;
            



            nameRunText = newComment.DisplayName;





            namelinkForeground = getBrush(getDisplayNameColor(newComment));
            


            
            nameLinkCommand = getShowUserCommand(newComment);

            idRunText = newComment.DisplayId;
            idlinkCommand = getShowUserCommand(newComment);





            setCommentBody(newComment);


            setScoreDescription(newComment);

            if (newComment.No > 0)
            {
                replyRun.Text = "返信";
            }
            else
            {
                replyRun.Text = "";
            }
            replylinkCommand = newComment.ReplyRequestCommand;

            displayTimeText = newComment.DisplayTime;
            

            

            
        }

        private Color getDisplayNameColor(NicoViewCommentViewModel newComment)
        {
            switch (newComment.UserType)
            {
                case CommentUserType.Manager:
                    return Settings.Default.ManagerUserNameColor.AsWindowsMediaColor();


                case CommentUserType.Owner:
                    return Settings.Default.OwnerUserNameColor.AsWindowsMediaColor();


                case CommentUserType.Premium:
                    return Settings.Default.PremiumUserNameColor.AsWindowsMediaColor();


                case CommentUserType.General:
                    if (newComment.IsAnonymous)
                    {
                        return Settings.Default.AnonymousUserNameColor.AsWindowsMediaColor();
                    }
                    else
                    {
                        return Settings.Default.GeneralUserNameColor.AsWindowsMediaColor();
                    }


                default:
                    return Settings.Default.AnonymousUserNameColor.AsWindowsMediaColor();

            }
        }



        private bool checkUpdateNo(NicoViewCommentViewModel newComment)
        {
            return this.noRunText != newComment.DisplayNo;
        }

        private bool checkUpdateName(NicoViewCommentViewModel newComment)
        {
            return this.nameRunText != newComment.DisplayName;
        }

        private bool checkUpdateNameColor(NicoViewCommentViewModel newComment)
        {
            return this.nameColor != getDisplayNameColor( newComment);
        }

        private bool checkUpdateCommentBody(NicoViewCommentViewModel newComment)
        {
            return (this.comment != newComment) || planeTextColor != getDisplayPlaneTextColor( newComment);
        }


        private static void setProperty<T>(ref T target, T val)
        {
            target = val;
        }




        private Span bodySpan;

        private Run noRun;

        private Run nameRun;

        private Run scoreDescriptionRun;

        private Run replyRun;

        private Hyperlink namelink;

        private Run idRun;

        private Hyperlink idlink;

        private Hyperlink replylink;

        private Run displayTime;

        private void setCommentBody(NicoViewCommentViewModel newComment)
        {
            this.bodySpan.Inlines.Clear();

            newComment.DisplayBody.ToDisplayString((bodyText) =>
            {
                parseComment(this.Inlines, bodyText, newComment);
            });



        }

        private void setScoreDescription(NicoViewCommentViewModel newComment)
        {

            if (newComment.CommentScoreRank != CommentScoreRank.None)
            {
                this.scoreDescriptionRun.Text = newComment.CommentScoreRank.GetDescription() + "/スコア:" + newComment.Score;
                this.scoreDescriptionRun.Foreground = getBrush(getScoreDescriptionColor(newComment));
            }
            else
            {
                this.scoreDescriptionRun.Text = "";
            }
            
            
        }

        private Color getScoreDescriptionColor(NicoViewCommentViewModel newComment)
        {
            switch (newComment.Comment.CommentScoreRank)
            {
                case CommentScoreRank.None:
                    return Settings.Default.NoneNgColor.AsWindowsMediaColor();

                case CommentScoreRank.LittleNg:
                    return Settings.Default.LittleNgColor.AsWindowsMediaColor();

                case CommentScoreRank.MediumNg:
                    return Settings.Default.MediumNgColor.AsWindowsMediaColor();

                case CommentScoreRank.ManyNg:
                    return Settings.Default.ManyNgColor.AsWindowsMediaColor();

                default:
                    return Settings.Default.NoneNgColor.AsWindowsMediaColor();

            }
        }
        
        private  Hyperlink createNoSingleAnchorLink(INicoCommentBodyText text)
        {

            Hyperlink link = createGeneralLink(text.DisplayText);

            link.Click += (s, e) =>
            {
                this.comment.SingleNoAnchor(new NicoCommentSingleNoAnchorMessage(this.comment, text.GetAnchor()));

            };
            
            
            return link;
        }

        private Hyperlink createNoMultiAnchorLink(INicoCommentBodyText text)
        {

            Hyperlink link = createGeneralLink(text.DisplayText);

            link.Click += (s, e) =>
            {
                var anchors = text.GetRangeAnchor();
                this.comment.MultiNoAnchor(new NicoCommentMultiNoAnchorMessage(this.comment, anchors.Item1, anchors.Item2));
            };
            
            return link;
        }

        private  Hyperlink createUrlLink(INicoCommentBodyText text)
        {
            Hyperlink link = createGeneralLink(text.DisplayText);
            link.ToolTip = "クリックするとURLを開きます";

            link.Click += (s, e) =>
            {
                var videoMatch = Regex.Match(text.DisplayText, NicoFormat.VIDEOID_FORMAT);

                if (videoMatch.Success)
                {
                    this.comment.VideoShow(new NicoCommentVideoShowMessage(this.comment, videoMatch.Value));
                }
                else
                {
                    this.comment.UrlShow(new NicoCommentUrlShowMessage(this.comment, text.AsUrl()));
                }


            };
            return link;
        }

        private Hyperlink createVideoIdLink(INicoCommentBodyText text)
        {
            Hyperlink link = createGeneralLink(text.DisplayText);
            link.ToolTip = "クリックすると動画情報を開きます";
            link.Click += (s, e) =>
            {
                this.comment.VideoShow(new NicoCommentVideoShowMessage(this.comment, text.DisplayText));
            };
            return link;
        }

        private Hyperlink createChannelIdLink(INicoCommentBodyText text)
        {
            Hyperlink link = createGeneralLink(text.DisplayText);
            link.ToolTip = "チャンネル番号";
            link.Click += (s,e)=>
            {
                this.comment.ChannelShow(new NicoCommentChannelShowMessage(this.comment, text.DisplayText));
                
            };
            return link;
        }

        private Hyperlink createLiveIdLink(INicoCommentBodyText text)
        {
            Hyperlink link = createGeneralLink(text.DisplayText);
            link.ToolTip = "生放送番号";
            link.Click += (s, e) =>
            {
                this.comment.LiveShow(new NicoCommentLiveShowMessage(this.comment, text.DisplayText));

            };
            
            return link;
        }

        private Hyperlink createCommunityLink(INicoCommentBodyText text)
        {
            Hyperlink link = createGeneralLink(text.DisplayText);
            link.ToolTip = "コミュニティ番号";
            link.Click += (s, e) =>
            {
                this.comment.CommunityShow(new NicoCommentCommunityShowMessage(this.comment, text.DisplayText));

            };
            return link;
        }
        private LineBreak createLineBreak(INicoCommentBodyText text)
        {
            return new LineBreak();
        }
        /*
        private static string _htmlTagConvert(string target)
        {
            if (Settings.Default.DifficultCommentReduction) return HTMLTAG_REGEX.Replace(target, " ");
            else return target;
        }
        */

        private Hyperlink createGeneralLink(string text)
        {
            Run run = new Run(text);
            Hyperlink link = new Hyperlink(run);
            link.TextDecorations = null;
            link.FontSize += planeTextPlus;
            return link;
        }

        private Run createPlaneText(INicoCommentBodyText text,Color planeTextColor)
        {
            //target = PLANETEXT_REPLACE_TO_EMPTY_REGEX.Replace(target, "");



            Run run = new Run(text.DisplayText);

            run.Foreground = getBrush(planeTextColor);
            run.FontSize += planeTextPlus;
            return run;

        }

        private void parseComment(ICollection<Inline> inlines, INicoCommentBodyText text,NicoViewCommentViewModel newComment)
        {


            switch (text.BodyType)
            {
                case NicoCommentBodyTextType.LineBreak:
                    bodySpan.Inlines.Add(createLineBreak(text));
                    break;

                case NicoCommentBodyTextType.LiveId:
                    bodySpan.Inlines.Add(createLiveIdLink(text));
                    break;


                case NicoCommentBodyTextType.Url:
                case NicoCommentBodyTextType.AnchorHtmlTag:
                    bodySpan.Inlines.Add(createUrlLink(text));
                    break;

                case NicoCommentBodyTextType.VideoId:
                    bodySpan.Inlines.Add(createVideoIdLink(text));
                    break;

                case NicoCommentBodyTextType.SingleNoAnchor:
                    bodySpan.Inlines.Add(createNoSingleAnchorLink(text));
                    break;

                case NicoCommentBodyTextType.RangeNoAnchor:
                    bodySpan.Inlines.Add(createNoMultiAnchorLink(text));
                    break;

                default:
                    if (text.HasCurrentBodyText)
                    {
                        bodySpan.Inlines.Add(createPlaneText(text, getDisplayPlaneTextColor(newComment)));
                    }
                    break;
                        
            }
            

        }


        private Color getDisplayPlaneTextColor(NicoViewCommentViewModel newComment)
        {
            switch (newComment.UserType)
            {
                case CommentUserType.Manager:
                    return Settings.Default.ManagerUserPlaneTextColor.AsWindowsMediaColor();

                case CommentUserType.Owner:
                    return Settings.Default.OwnerUserPlaneTextColor.AsWindowsMediaColor();

                case CommentUserType.Premium:
                case CommentUserType.General:
                    return newComment.Comment.IsJapanese ? Settings.Default.GeneralUserPlaneTextColor.AsWindowsMediaColor() : Settings.Default.ForeignUserPlaneTextColor.AsWindowsMediaColor();

                default:
                    return Settings.Default.GeneralUserPlaneTextColor.AsWindowsMediaColor();

            }
        }


        private static readonly Dictionary<Color, SolidColorBrush> brushCash = new Dictionary<Color, SolidColorBrush>();

        private static readonly double underMinus;

        private static readonly double nameTextPlus;

        private static readonly double planeTextPlus;



        private const string USER_INFO_TOOLTIP = "クリックするとユーザ情報を開きます";
    }
}
